package org.micode.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
public class BeanExtUtils extends org.springframework.beans.BeanUtils {

    private static final String PREFIX_SET = "set";
    private static final String PREFIX_GET = "get";

    /**
     * 通过字段名获取字段值
     */
    public static boolean findFieldByName(Object bean, String fieldName) {
        if (bean == null || fieldName == null || fieldName.trim().isEmpty()) {
            return false;
        }

        Class<?> cls = bean.getClass();
        Method[] methods = cls.getMethods();

        try {
            String fieldGetName = parseMethodName(fieldName, PREFIX_GET);
            Method theMethod = checkAndGetTheMethod(methods, fieldGetName);
            if (null != theMethod) {
                return true;
            }
        } catch (Exception e) {
            log.warn("findFieldByName() call failed, {}", e.getMessage());
        }
        return false;
    }

    public static void setFieldByName(Object bean, String fieldName, Object value) {
        if (bean == null || StringExtUtils.isAnyEmpty(fieldName)) {
            return;
        }

        Class<?> cls = bean.getClass();
        Method[] methods = cls.getMethods();

        String fieldSetName = parseMethodName(fieldName, PREFIX_SET);
        try {
            Method fieldSetMet = checkAndGetTheMethod(methods, fieldSetName);
            if (null != fieldSetMet) {
                fieldSetMet.invoke(bean, value);
            } else {
                log.warn("The GET method not found, {}.{}", cls.getSimpleName(), fieldSetName);
            }
        } catch (Exception e) {
            log.warn("setFieldValueByName() call failed({})", fieldSetName, e);
        }
    }

    /**
     * 获取属性名数组
     */
    public static String[] getSelfAllFiledName(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldNames[i] = fields[i].getName();
        }
        return fieldNames;
    }

    /**
     * 获取属性名数组
     */
    public static String[] getAllFiledNameIncludeSuperclass(Class<?> clazz) {
        List<Field> fields = readFieldsIncludeSuperclass(clazz);
        String[] fieldNames = new String[fields.size()];
        for (int i = 0; i < fields.size(); i++) {
            fieldNames[i] = fields.get(i).getName();
        }
        return fieldNames;
    }

    /**
     * 通过字段名获取字段值
     */
    public static Object getFieldValueByName(Object bean, String fieldName) {
        if (bean == null || fieldName == null || fieldName.trim().isEmpty()) {
            return null;
        }

        Class<?> cls = bean.getClass();
        Method[] methods = cls.getMethods();

        try {
            String fieldGetName = parseMethodName(fieldName, PREFIX_GET);
            Method theMethod = checkAndGetTheMethod(methods, fieldGetName);
            if (null != theMethod) {
                return theMethod.invoke(bean);
            } else {
                log.info("The GET method not found, {}.{}", cls.getSimpleName(), fieldGetName);
            }
        } catch (Exception e) {
            log.warn("getFieldValueByName() call failed, {}", e.getMessage());
        }
        return null;
    }

    /**
     * 检测所有字段是不是null,不包含父类的字段
     */
    public static boolean checkAllFieldValueIsNull(Object bean, Class<?> beanClazz) {
        boolean result = true;
        if (bean == null) {
            return true;
        }
        Method[] methods = beanClazz.getMethods();
        Field[] fields = beanClazz.getDeclaredFields();
        for (Field field : fields) {
            result = result && isNullField(bean, field, methods);
        }
        return result;
    }

    private static boolean isNullField(Object bean, Field field, Method[] methods) {
        boolean isNull = true;
        try {
            String fieldGetName = parseMethodName(field.getName(), PREFIX_GET);
            Method fieldGetMet = checkAndGetTheMethod(methods, fieldGetName);
            if (null != fieldGetMet) {
                Object fieldVal = fieldGetMet.invoke(bean);
                isNull = (null == fieldVal);
                if (fieldVal != null) {
                    isNull = fieldVal instanceof String && StringUtils.isEmpty((String) fieldVal);
                }
            }
        } catch (Exception e) {
            log.warn("checkAllFieldValueIsNull() call failed, {}", e.getMessage());
        }
        return isNull;
    }

    /**
     * 检测所有字段是不是null, 包含父类的字段
     */
    public static boolean checkAllFieldValueIsNullIncludeSuperclass(Object bean) {
        boolean result = true;
        if (bean == null) {
            return true;
        }

        Class<?> cls = bean.getClass();
        Method[] methods = cls.getMethods();
        List<Field> fields = readFieldsIncludeSuperclass(cls);
        for (Field field : fields) {
            result = result && isNullField(bean, field, methods);
        }
        return result;
    }

    private static List<Field> readFieldsIncludeSuperclass(Class<?> cls) {
        Field[] myFields = cls.getDeclaredFields();
        Class superClass = cls.getSuperclass();
        List<Field> fields = new ArrayList<>(Arrays.asList(myFields));
        if (null != superClass) {
            List<Field> superFields = Arrays.asList(superClass.getDeclaredFields());
            fields.addAll(superFields);
        }
        return fields;
    }

    /**
     * 拼接某属性的方法
     */
    private static String parseMethodName(String fieldName, String prefix) {
        if (null == fieldName || "".equals(fieldName)) {
            return null;
        }
        int startIndex = 0;
        if (fieldName.charAt(0) == '_')
            startIndex = 1;
        return prefix
                + fieldName.substring(startIndex, startIndex + 1).toUpperCase()
                + fieldName.substring(startIndex + 1);
    }

    /**
     * 判断是否存在某属性的方法
     */
    private static Method checkAndGetTheMethod(Method[] methods, String fieldGetMet) {
        for (Method met : methods) {
            if (fieldGetMet.equals(met.getName())) {
                return met;
            }
        }
        return null;
    }

    /**
     * 拷贝对象，可以指定忽略的字段
     *
     * @param source
     * @param target
     * @param ignoreProperties
     */
    public static void copyTo(Object source, Object target, String... ignoreProperties) {
        org.springframework.beans.BeanUtils.copyProperties(source, target, ignoreProperties);
    }

    /**
     * 拷贝对象
     *
     * @param source
     * @param copyTo
     * @param <T>
     * @return
     */
    public static <T> T copyObject(Object source, Class<T> copyTo) {
        if (source == null) {
            return null;
        }

        T t = org.springframework.beans.BeanUtils.instantiateClass(copyTo);
        org.springframework.beans.BeanUtils.copyProperties(source, t);
        return t;
    }

    public static void main(String[] args) {
        // {id=1, balance=999.8}
    }

}
